# coding=utf-8

from traverse.crawler_conf import CrawlerConf
from traverse.core.driver import Driver
from traverse.core.element_manager import PageManage
from traverse.common.log import Logger
from traverse.common.utils import ImgUtil
from traverse.common.utils import CrashUtil
import os
import time
import random
import re
import copy
'''
应用页面爬虫，遍历执行框架
'''


class Crawler(object):
    __REPORT_DIR = "./report"
    
    __TIME_OUT_PAGE = 300
    
    def __init__(self, serial, conf):
        self.serial = serial
        self.conf = CrawlerConf(conf)
        self.logger = Logger.getLogger(level=self.conf.logLevel)
        report_dir = self.conf.report_dir or self.__REPORT_DIR
        self.report_dir = os.path.join(os.path.abspath(report_dir), \
            time.strftime("%Y.%m.%d-%H.%M.%S",time.localtime(time.time())), self.serial)
        if not os.path.exists(self.report_dir):
            os.makedirs(self.report_dir)
        self.conf.report_dir = self.report_dir
        self.detail_dir = os.path.join(self.report_dir,'detail')
        if not os.path.exists(self.detail_dir):
            os.makedirs(self.detail_dir)
        self.driver = Driver(self.serial)
        self.logger.info('device screen size width:%s height:%s'%(self.driver.width, self.driver.height))
        self.page_manage = PageManage(self.driver, self.conf)
        self.action = [self.driver.page_left,
                       self.driver.page_right,self.driver.back]
        self.rand_time = 0 # 用于随机处理self.action
        self.curent_page = None
        self.action_flag = True
        self.time_out = self.__TIME_OUT_PAGE # 5分钟单页面超时操作
        self.last_page = None
        self.time_out = None
        
    def start(self):
        self.init() # 初始化相关数据
        self.start_guide()
        while True:
            self.deep_crawler(self.conf.maxDeepth)
            time.sleep(0.2)
    
    def init(self): # 初始化用来，清理相关数据
        app_path = self.conf.crash_attach_file.get(self.conf.app)
        self.driver.clear_forder(app_path + "logs/*")
    
    def start_app(self,app):
        if self.driver.get_current_app() != self.conf.app:
            self.driver.start_app(app)
        
    def start_guide(self):
        '''打开程序的首次引导'''
        self.driver.force_stop(self.conf.app)
        self.start_app(self.conf.app)
        time.sleep(10)
        if self.conf.startup_guide:
            self.exec_cmd_str(self.conf.startup_guide)
        
    def exec_cmd_str(self, cmdstr):
        '''执行命令字符串'''
        try:
            d = self.driver.device
            exec(cmdstr)
        except:
            pass
        
    
    def deep_crawler(self, max_depth=1):
        self.logger.info('refresh current page')     
        if not self.page_manage.refresh_page():
            self.logger.info('page not in app, go back')
            self.need_back()
        else:
            self.curent_page = self.page_manage.current_page
            if self.needSkipPage(): # 全局需要跳过的测试页面
                self.need_back()
                return
            available_eles = self.get_available_list()
            self.page_count_back(available_eles)
            self.logger.info("current page clickable element %s"%len(available_eles))
            if len(available_eles) != 0:
                self.dealElementAction(available_eles[0])
            else:
                self.need_back()
    
    def needSkipPage(self): # 需要跳过的测试页面
        if self.isUrlBlackList(): # urlBlackList 
            return True
        if self.conf.urlPageBlackList: # 跳过存在此种元素的页面
            if self.curent_page.is_contain_element(self.conf.urlPageBlackList):
                return True

    def isUrlBlackList(self): # 过滤ui黑名单
        activity = self.driver.get_current_activity()
        blackList = self.conf.urlBlackList
        if blackList:
            for urlblack in blackList:
                if re.match(urlblack, activity):
                    self.logger.info("current page %s is ui blacklist,need back"%activity)
                    return True
        
    def page_count_back(self, eles): #页面点击已经最大
        flag = True
        for ele in eles:
            if ele.count < self.conf.max_click_num:
                flag = False
                break
        if flag:
            self.need_back()    
    
    def need_back(self):
        self.driver.back()
        self.custom_back_deal()
        app = self.driver.get_current_app()
        if app == "StatusBar":  # 当前界面为状态栏
            self.driver.back()
        elif app != self.conf.app: # 当前界面未在测试应用上，需返回到测试app
            self.driver.start_app(self.conf.app)
    
    def time_out_back(self):
        '''单页面停留时间过长，返回'''
        if self.last_page is None:
            self.last_page = self.curent_page
            self.time_out = self.__TIME_OUT_PAGE
            self.time_out = time.time()
        if self.last_page:
            if self.last_page.uuid == self.curent_page.uuid and time.time() - self.time_out > self.__TIME_OUT_PAGE:
                self.last_page = None
                self.time_out = None
                self.driver.back()
    
    def custom_back_deal(self):
        '''自定义back键处理'''
        if self.conf.back_action:
            self.exec_cmd_str(self.conf.back_action)
            
    def saveScreen(self, ele):
        if self.conf.screenShot:
            file_path =  os.path.join(self.detail_dir, ele.get_screenshot_file_name()+"-"+str(time.time()) + ".png")
            self.driver.screenShot(file_path)
            if os.path.exists(file_path):
                try:
                    ImgUtil.tag_img(ele.bound, file_path)
                except:
                    self.logger.warning("screenshot error")
            else:
                self.logger.warning("screenshot failure: %s"%file_path)
        
    def get_available_list(self): # 过滤相关元素，黑名单，对元素按已点击次数进行从小到大的排序
        available_eles = self.get_selected_list()
        black_eles = self.get_black_element_list()
        diff_eles = self.diff_eles(available_eles, black_eles)
        def sort(ele1,ele2):
            if ele1.count > ele2.count:
                return 1
            elif ele1.count < ele2.count:
                return -1
            else:
                return 0
        self.logger.info('clicked elements total: ' + str(len(diff_eles)))
        return sorted(diff_eles, cmp=sort)
#         return self.__re_sort(sorted(diff_eles, cmp=sort))
    
    def __re_sort(self, raw_list):
        '''按优先点击顺序排'''
        priority_eles = self.get_priority_click_list()
        for i in priority_eles:
            print i.ele.get('resource-id')
        result_list = copy.copy(raw_list)
        for ele in raw_list:
            for ele2 in priority_eles:
                if ele.uuid == ele2.uuid and ele.count==0:                    
                    result_list.remove(ele)
                    result_list.insert(0, ele)
        return result_list   
        
    
    def get_priority_click_list(self):
        '''获取优先点击元素'''
        eles = self.curent_page.get_select_nodes(self.conf.priorityClickElement)
        return eles
    
    def get_selected_list(self):
        '''获取可选择的元素'''
        eles = self.curent_page.get_select_nodes(self.conf.selectedList)
        return eles
    
    def get_black_element_list(self):
        '''获取黑名单元素'''
        eles = self.curent_page.get_select_nodes(self.conf.blackElementList)
        return eles
        
    def beforeAction(self, ele):
        '''点击前触发'''
        self.saveScreen(ele)
    
    def afterAction(self, ele):
        '''点击后触发'''
        self.trigger_action()
        self.get_crash()
        self.time_out_back()
        self.randon_action()
    
    def get_crash(self):
        '''点击后检查相关crash信息'''
        app_path = self.conf.crash_attach_file.get(self.conf.app)
        crash_path = os.path.join(self.report_dir, "crash")
        if not os.path.exists(crash_path):
            os.makedirs(crash_path)  
        CrashUtil.get_crash_info(self.driver.device, self.conf.app, app_path, crash_path)
        
    def trigger_action(self):
        '''自定义触发形为，如处理弹窗，处理登录'''
        if self.conf.trigger_action:
            for items in self.conf.trigger_action:
                for key in items.keys():
                    self.exec_cmd_str(items[key])

    def randon_action(self):
        '''定义随机行为触发方式'''
        rand_time = random.randint(30,60)
        if time.time() - self.rand_time > rand_time:
            rand_sed = random.randint(1, len(self.action))
            self.action[rand_sed-1]()
            self.rand_time = time.time()
    
    def dealElementAction(self, ele):
        self.logger.info("click element:%s count:%s"%(ele.uuid,ele.count))
        self.beforeAction(ele)
        position = ele.center()
        if self.action_flag: # 随机标记，用于点击 长按
            self.driver.click(position[0], position[1])
            self.action_flag = not self.action_flag
        else:
            self.driver.long_click(position[0], position[1]) 
            self.action_flag = not self.action_flag
        self.curent_page.click(ele, self.detail_dir)
        self.afterAction(ele)
    
    def diff_eles(self, save_eles, diff_eles):
        '''计算元素差集，但不改变传入的集合'''
        if diff_eles:
            for ele in diff_eles:
                for ele1 in save_eles:
                    if ele.uuid == ele1.uuid:
#                         print ele1.ele.attrib
                        save_eles.remove(ele1)
        return save_eles 
 
if __name__ == '__main__':
    Crawler('N7CMJJRWW4SGQW85',"./conf.yml").start()